package hydrap2p.modulemanager;

import hydrap2p.ServiceLocation;
import hydrap2p.helper.Pinger;
import hydrap2p.helper.Utility;
import hydrap2p.modulemanager.listen.Listeners;

import java.io.FileNotFoundException;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;

public class ModuleController {

	private final Map<String, ModuleStatus> status;

	private ModuleManagerServer server; // reference to shutdown server
	
	private Pinger pinger;

	public ModuleController() {
		status = new TreeMap<String, ModuleStatus>();
		Listeners.controller = this;
		server = null;
		pinger = null;
		load();
	}
	
	public void load() {
		ConfigMunger configger = new ConfigMunger();
		Map<String, ModuleConfig> config = configger.load();
		synchronized(this) 
		{
			for (Map.Entry<String, ModuleConfig> conf : config.entrySet())
			{
				ModuleStatus status=getStatus(conf.getKey(), "local");
				if (status!=null && status instanceof ModuleStatusLocal)
				{
					((ModuleStatusLocal)status).setConfig(conf.getValue());
				}
				else
				{
					ModuleStatusLocal tempLocal = new ModuleStatusLocal(conf.getValue(), this, configger);
					addModule(conf.getKey(), "local", tempLocal);
				}
			}
		}
	}
	
	public void start(String name) {
		start(name, "local");
	}

	public void start(String name, String server) {
		ModuleStatus temp = getStatus(name, server);
		if (temp != null) {
			temp.start();
		}
	}
	
	public void stop(String name) {
		stop(name, "local");
	}

	public void stop(String name, String server) {
		ModuleStatus temp = getStatus(name, server);
		if (temp != null) {
			temp.stop();
		}
	}

	public void serviceStarted(String name, ServiceLocation location) {
		for (ModuleStatus module : this.status.values())
		{
			if (module instanceof ModuleStatusLocal)
			{
				((ModuleStatusLocal)module).serviceStarted(name, location);
			}
		}
	}

	public void serviceStopped(String name) {
		for (ModuleStatus module : this.status.values())
		{
			if (module instanceof ModuleStatusLocal)
			{
				((ModuleStatusLocal)module).serviceStopped(name);
			}
		}
	}
	
	/**
	 * Get the best status known for any module by this name
	 * @param name
	 * @return
	 */
	public ModuleStatus getStatus(String name) {
		ModuleStatus best=null;
		synchronized(this)
		{
			for (ModuleStatus status : this.status.values())
			{
				if (status.getInfo().name.equals(name))
					if (best==null || (status.getInfo().server.equals("local") || best.getStatus().compareTo(status.getStatus())<0))
						best=status;
			}
		}
		return best;
	}

	public ModuleStatus getStatus(String name, String server) {
		ModuleStatus temp=null;
		synchronized(this)
		{
			if (status.containsKey(name+"@"+server))
				temp=status.get(name+"@"+server);
		}
		return temp;
	}

	public ModuleStatus getModuleForService(String service) {

		ModuleStatus best=null;
		synchronized(this)
		{
			for (ModuleStatus status : this.status.values())
			{
				if (status.providedServices().contains(service))
					if (best==null || (status.getInfo().server.equals("local") || best.getStatus().compareTo(status.getStatus())<0))
						best=status;
			}
		}
		return best;
	}

	public List<ModuleInfo> getAllModules() {
		load();
		List<ModuleInfo> ret = new LinkedList<ModuleInfo>();
		synchronized (this) {
			for (ModuleStatus module : status.values()) {
				ret.add(module.getInfo());
			}
		}
		return ret;
	}

	public ModuleInfo getModuleInfo(String name, String server) {
		ModuleStatus status = getStatus(name, server);
		if (status == null)
			return null;

		ModuleInfo temp = new ModuleInfo();
		temp.autostart = status.getAutostart();
		if (status instanceof ModuleStatusLocal)
		{
			temp.server="local";
			temp.name = name;
			temp.isRemote = false;
		}
		else
		{
			temp.server="remote";
			temp.name = temp.name;
			temp.isRemote = true;
		}
		
		temp.running = getStatus(name, server).getStatus() != ModuleStatusType.STOPPED;
		return temp;
	}

	public void setModuleAutostart(String name, String server, boolean autostart) {
		ModuleStatus status = getStatus(name, server);
		if (status == null)
			return;
		status.setAutostart(autostart);
	}

	public void autostartAll() {
		List<ModuleStatus> modules = new LinkedList<ModuleStatus>(status.values());
		for (ModuleStatus conf : modules) {
			if (conf.getAutostart())
				start(conf.getInfo().name, conf.getInfo().server);
		}
	}

	public void shutdown() {
		List<ModuleStatus> modules=new LinkedList<ModuleStatus>(status.values());
		for (ModuleStatus module : modules) {
			if (!module.getInfo().name.equals("ServiceManager") && module instanceof ModuleStatusLocal)
				module.stop();
		}
		if (pinger!=null)
			pinger.shutdown();
		stop("ServiceManager", "local");
		if (server != null)
			server.stoplistener();
		Listeners.shutdown();
	}

	public void setServer(ModuleManagerServer moduleManagerServer) {
		this.server = moduleManagerServer;
	}
	
	public void addModule(String name, String server, ModuleStatus status)
	{
		synchronized(this)
		{
			this.status.put(name+"@"+server, status);
		}
	}
	
	public void updateModule(String name, String server, ModuleStatus status)
	{
		synchronized(this)
		{
			this.status.put(name+"@"+server, status);
		}
	}
	
	public void delModule(String name, String server)
	{
		synchronized(this)
		{
			this.status.remove(name+"@"+server);
		}
	}
	
	/**
	 * When the ServiceManager is up, start pinging it
	 */
	public void startPinger()
	{
		try {
			pinger=new MyPinger("ServiceManager", new ServiceLocation("localhost", Utility.getManagerPort()), this);
		} catch (NumberFormatException e) {
			// Could not locate ServiceManager
		} catch (FileNotFoundException e) {
			// Could not locate ServiceManager
		}
	}
	
	private class MyPinger extends Pinger {
		private final ModuleController controller;
		public MyPinger(String name, ServiceLocation location, ModuleController controller) {
			super(name, location);
			this.controller=controller;
		}

		@Override
		public void failure(String name) {
			System.err.println("Failed to ping ServiceManager, shutting down");
			controller.shutdown();
		}
		
	}
}
